package com.bite.system.service.user.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bite.common.core.constants.Constants;
import com.bite.common.core.enums.ResultCode;
import com.bite.common.security.exception.ServiceException;
import com.bite.system.domain.exam.Exam;
import com.bite.system.domain.exam.ExamUserGroup;
import com.bite.system.domain.exam.UserExam;
import com.bite.system.domain.user.dto.UserGroupExamUpdateDTO;
import com.bite.system.domain.user.User;
import com.bite.system.domain.user.UserGroup;
import com.bite.system.domain.user.UserGroupUser;
import com.bite.system.domain.user.dto.*;
import com.bite.system.domain.user.vo.UserGroupDetailVO;
import com.bite.system.domain.user.vo.UserGroupVO;
import com.bite.system.domain.user.vo.UserVO;
import com.bite.system.manager.UserCacheManager;
import com.bite.system.mapper.exam.ExamMapper;
import com.bite.system.mapper.exam.ExamUserGroupMapper;
import com.bite.system.mapper.user.UserExamMapper;
import com.bite.system.mapper.user.UserGroupMapper;
import com.bite.system.mapper.user.UserGroupUserMapper;
import com.bite.system.mapper.user.UserMapper;
import com.bite.system.service.user.IUserGroupService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
* @author 17450
* @description 针对表【tb_user_group】的数据库操作Service实现
* @createDate 2025-06-18 16:17:03
*/
@Service
public class UserGroupServiceImpl extends ServiceImpl<UserGroupUserMapper, UserGroupUser>
        implements IUserGroupService {

    @Autowired
    private UserGroupMapper userGroupMapper;

    @Autowired
    private UserGroupUserMapper userGroupUserMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private UserExamMapper userExamMapper;

    @Autowired
    private ExamUserGroupMapper examUserGroupMapper;

    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private UserCacheManager userCacheManager;


    @Override
    public List<UserGroupVO> list(UserGroupQueryDTO userGroupQueryDTO) {
        return userGroupMapper.list(userGroupQueryDTO);
    }

    @Override
    public UserGroupDetailVO detail(Long userGroupId) {
        UserGroup userGroupById = getUserGroupById(userGroupId);
        Set<Long> userIdSet = userGroupUserMapper.selectUserIdByGroupIdList(Set.of(userGroupId));
        UserGroupDetailVO userGroupDetailVO = new UserGroupDetailVO();
        BeanUtil.copyProperties(userGroupById, userGroupDetailVO);

        if (CollectionUtil.isEmpty(userIdSet)) return userGroupDetailVO;
        List<User> userList = userMapper.selectList(new LambdaQueryWrapper<User>()
                .select(User::getUserId, User::getNickName, User::getPhone)
                .in(User::getUserId, userIdSet));
        userGroupDetailVO.setUserVOList(BeanUtil.copyToList(userList, UserVO.class));
        return userGroupDetailVO;
    }

    @Override
    public Long add(UserGroupAddDTO userGroupAddDTO) {
        checkUserGroupParams(userGroupAddDTO, false);
        UserGroup userGroup = new UserGroup();
        BeanUtil.copyProperties(userGroupAddDTO, userGroup);
        userGroupMapper.insert(userGroup);
        return userGroup.getUserGroupId();
    }

    @Override
    public int edit(UserGroupEditDTO userGroupEditDTO) {
        checkUserGroupParams(userGroupEditDTO, true);
        Long userGroupId = userGroupEditDTO.getUserGroupId();
        checkUserGroupBeforeSave(Set.of(userGroupId));
        UserGroup userGroupById = getUserGroupById(userGroupId);
        BeanUtil.copyProperties(userGroupEditDTO, userGroupById);
        userGroupMapper.updateById(userGroupById);
        return Constants.TRUE;
    }

    @Override
    public int delete(Long userGroupId) {
        checkUserGroupBeforeSave(Set.of(userGroupId));
        getUserGroupById(userGroupId);
        List<Long> examIdList = examUserGroupMapper.selectList(new LambdaQueryWrapper<ExamUserGroup>()
                .select(ExamUserGroup::getExamId)
                .eq(ExamUserGroup::getUserGroupId, userGroupId))
                .stream().map(ExamUserGroup::getExamId).toList();

        Set<Long> effectUserIdSet = new HashSet<>();
        deleteUserGroupByExamId(examIdList, true, effectUserIdSet);
        userCacheManager.refreshUserExam(effectUserIdSet);
        return userGroupMapper.deleteById(userGroupId);
    }

    @Override
    public boolean userUpdate(UserGroupUserUpdateDTO userGroupUserUpdateDTO) {
        checkUserGroupBeforeSave(Set.of(userGroupUserUpdateDTO.getUserGroupId()));
        Long userGroupId = userGroupUserUpdateDTO.getUserGroupId();
        List<Long> userIdList = userGroupUserUpdateDTO.getUserIdList();
        Set<Long> effectUserIdSet = getEffectUserIdSet(userGroupUserUpdateDTO);
        // 更新之前先要将对应的用户组先更新为正确的
        updateUserGroupUser(userGroupId, userIdList);
        // 找到受影响的竞赛
        List<ExamUserGroup> examUserGroupList = examUserGroupMapper.selectList(new LambdaQueryWrapper<ExamUserGroup>()
                .select(ExamUserGroup::getExamId, ExamUserGroup::getUserGroupId)
                .eq(ExamUserGroup::getUserGroupId, userGroupId));
        List<Long> effectExamIdList = examUserGroupList.stream().map(ExamUserGroup::getExamId).toList();
        deleteUserGroupByExamId(effectExamIdList, true, effectUserIdSet);
        Map<Long, List<ExamUserGroup>> examUserGroupMap = examUserGroupList.stream().collect(Collectors.groupingBy(ExamUserGroup::getExamId));
        insertUserGroup(examUserGroupMap,true, effectUserIdSet);
        return true;
    }

    private void updateUserGroupUser(Long userGroupId, List<Long> userIdList) {
        userGroupUserMapper.deleteByGroupIds(Set.of(userGroupId));
        List<UserGroupUser> userGroupList = userIdList.stream().map(userId -> {
            UserGroupUser userGroupUser = new UserGroupUser();
            userGroupUser.setUserGroupId(userGroupId);
            userGroupUser.setUserId(userId);
            return userGroupUser;
        }).toList();
        if (CollectionUtil.isNotEmpty(userGroupList)) userGroupUserMapper.insertBatch(userGroupList);
    }

    private Set<Long> getEffectUserIdSet(UserGroupUserUpdateDTO dto) {
        return getEffectUserIdSet(dto.getUserGroupId(), new HashSet<>(dto.getUserIdList()));
    }

    // 获取到受影响的用户集合
    private Set<Long> getEffectUserIdSet(Long userGroupId, Set<Long> userIdSet) {
        Set<Long> oldUserIdSet = userGroupUserMapper.selectUserIdByGroupIdList(Set.of(userGroupId));
        Set<Long> newUserIdSet = new HashSet<>(userIdSet);
        // 交集
        Set<Long> interSet = new HashSet<>(oldUserIdSet);
        interSet.retainAll(newUserIdSet);
        // 并集
        Set<Long> unionSet = new HashSet<>(oldUserIdSet);
        unionSet.addAll(newUserIdSet);

        unionSet.removeAll(interSet);
        return unionSet;
    }

    @Override
    public int examUpdate(UserGroupExamUpdateDTO examUserGroupUpdateDTO) {
        checkUserGroupBeforeSave(examUserGroupUpdateDTO.getUserGroupIds());
        Exam exam = examMapper.selectById(examUserGroupUpdateDTO.getExamId());
        checkExamBeforeSave(exam);
        // 先删除原来的 然后再添加回去
        Long examId = examUserGroupUpdateDTO.getExamId();
        if (examId == null) throw new ServiceException(ResultCode.EXAM_NOT_EXISTS);
        Set<Long> effectUserIdSet = new HashSet<>();
        deleteUserGroupByExamId(List.of(examId), false, effectUserIdSet);
        Map<Long, List<ExamUserGroup>> examUserGroupMap = new HashMap<>();
        List<ExamUserGroup> userGroupList = examUserGroupUpdateDTO.getUserGroupIds().stream().map(id -> {
            ExamUserGroup examUserGroup = new ExamUserGroup();
            examUserGroup.setExamId(examId);
            examUserGroup.setUserGroupId(id);
            return examUserGroup;
        }).toList();
        examUserGroupMap.put(examId, userGroupList);
        return insertUserGroup(examUserGroupMap, false, effectUserIdSet);
    }

    /**
     * 插入竞赛与用户、竞赛与用户组的关系 如果isUserGroupUpdate = true 那么就不插入用户组与竞赛的关系
     * @param examUserGroupMap  用户组与竞赛的关系
     * @param isUserGroupUpdate 是否是用户组里面的详情更新
     * @param effectUserIdSet 受影响的用户Id
     * @return
     */
    private int insertUserGroup(Map<Long, List<ExamUserGroup>> examUserGroupMap, boolean isUserGroupUpdate, Set<Long> effectUserIdSet) {

        // 添加用户组
        if (!isUserGroupUpdate) {
            List<ExamUserGroup> examGroupList = new ArrayList<>();
            examUserGroupMap.forEach((examId, examUserGroupList) -> {
                examGroupList.addAll(examUserGroupList);
            });
            if (CollectionUtil.isNotEmpty(examGroupList)) {
                examUserGroupMapper.insertBatch(examGroupList);
            }
        }


        // 获取竞赛与用户的关系   value必须用Set 用户组用户可能会重复
        Map<Long, Set<Long>> examUserIdMap = new HashMap<>(); // key: examId, val userIdSet
        examUserGroupMap.forEach((examId, examUserGroupList) -> {
            Set<Long> userIdSet = Set.of();
            Set<Long> userGroupIdSet = examUserGroupList.stream().map(ExamUserGroup::getUserGroupId).collect(Collectors.toSet());
            if (CollectionUtil.isNotEmpty(userGroupIdSet)) {
                userIdSet = userGroupUserMapper.selectUserIdByGroupIdList(userGroupIdSet);
            }
            examUserIdMap.put(examId, userIdSet);
        });

        // 转换结果 使其可以批量插入
        List<UserExam> insertData = new ArrayList<>();
        examUserIdMap.forEach((examId, userIdSet) -> {
            List<UserExam> result = userIdSet.stream().map(userId -> {
                UserExam userExam = new UserExam();
                userExam.setExamId(examId);
                userExam.setUserId(userId);
                return userExam;
            }).toList();
            insertData.addAll(result);
        });

        int result = CollectionUtil.isEmpty(insertData) ? 1 : userExamMapper.saveBatch(insertData);
        // 更新完后再刷新缓存
        Set<Long> userIdSet = insertData.stream().map(UserExam::getUserId).collect(Collectors.toSet());
        userIdSet.addAll(effectUserIdSet);
        userCacheManager.refreshUserExam(userIdSet);
        return result;
    }



    /**
     * 删除竞赛与用户、竞赛与用户组的关系 如果isUserGroupUpdate = true 那么就不删除用户组与竞赛的关系
     * @param examIdList
     * @param isUserGroupUpdate 是否是用户组里面的详情更新
     */
    private Long deleteUserGroupByExamId(List<Long> examIdList, boolean isUserGroupUpdate, Set<Long> effectUserIdSet) {
        if (CollectionUtil.isEmpty(examIdList)) return 0L;
        // 删除用户组与竞赛的关系
        if (!isUserGroupUpdate) {
            examUserGroupMapper.delete(new LambdaQueryWrapper<ExamUserGroup>()
                    .in(ExamUserGroup::getExamId, examIdList));
        }
        // 获取受影响的用户Id
        effectUserIdSet.addAll(userExamMapper.selectUserIdByExamIdList(examIdList));
        // 删除用户竞赛关系
        return userExamMapper.deleteByExamIdList(examIdList);
    }

    private UserGroup getUserGroupById(Long userGroupId) {
        if (userGroupId == null) throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        UserGroup userGroup = userGroupMapper.selectOne(new LambdaQueryWrapper<UserGroup>()
                .eq(UserGroup::getUserGroupId, userGroupId));
        if (userGroup == null) throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        return userGroup;
    }

    private void checkUserGroupParams(UserGroupAddDTO dto, boolean isEdit) {
        String nickName = dto.getNickName();
        String introduce = dto.getIntroduce();
        if (StrUtil.isEmpty(nickName)) throw new ServiceException(ResultCode.FAILED_USER_GROUP_NICKNAME_EMPTY);
        if (StrUtil.isEmpty(introduce)) throw new ServiceException(ResultCode.FAILED_USER_GROUP_INTRODUCE_EMPTY);
        if (isEdit) return;
        UserGroup userGroup = userGroupMapper.selectOne(new LambdaQueryWrapper<UserGroup>()
                .eq(UserGroup::getNickName, nickName));
        if (userGroup != null) throw new ServiceException(ResultCode.FAILED_USER_GROUP_NICKNAME_REPEAT);
    }

    private void checkExamBeforeSave(Exam exam) {
        LocalDateTime now = LocalDateTime.now();
        if (now.isAfter(exam.getStartTime()) && now.isBefore(exam.getEndTime())
                && Constants.TRUE.equals(exam.getStatus())) {
            throw new ServiceException("竞赛id["+exam.getExamId()+"] 正在比赛 不能调节相关的用户组");
        }
    }

    private void checkUserGroupBeforeSave(Set<Long> userGroupIdSet) {
        if (CollectionUtil.isEmpty(userGroupIdSet)) return;
        Set<Long> examIdSet = examUserGroupMapper.selectList(new LambdaQueryWrapper<ExamUserGroup>()
                .select(ExamUserGroup::getExamId)
                .in(ExamUserGroup::getUserGroupId, userGroupIdSet))
                .stream().map(ExamUserGroup::getExamId).collect(Collectors.toSet());
        // 每一个竞赛都必须已经结束或者未开赛
        if (CollectionUtil.isEmpty(examIdSet)) return;
        List<Exam> exams = examMapper.selectBatchIds(examIdSet);
        for (Exam exam : exams) {
            checkExamBeforeSave(exam);
        }
    }
}




